#pragma once
#include <iostream>
#include <unordered_map>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>
#include <signal.h>
#include <cstring>
#include <sys/epoll.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include "send.hpp"
#define NUM 1024
class Funtion
{
public:
    // 默认构造函数
    Funtion() = default;
    // 设置非阻塞
    void Setftl(int sock)
    {
        int fl = fcntl(sock, F_GETFL);
        if (fl < 0)
            std::cout << "fcntl fail:" << strerror(errno) << std::endl;
        fcntl(sock, F_SETFL, fl | O_NONBLOCK);
    }
    // 设置端口复用
    void SetPort(int sock)
    {
        int opt = 1;
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }
    // 处理事件
    void HandlEvent(epoll_event *event, int sock, int num, std::unordered_map<int, Data *>& mmp)
    {
        for (size_t i = 0; i < num; i++)
        {
            auto cur = mmp.find(event[i].data.fd);
            if(cur != mmp.end())
            {
                if (event[i].events & EPOLLIN)
                {
                    std::cout << "文件描述符:" << event[i].data.fd << "就绪" << std::endl;
                    Recv(event[i].data.fd, mmp, sock);
                }
                if(event[i].events & EPOLLOUT)
                {
                    Write(event[i].data.fd, mmp);
                }
            }
            else
            {
                std::cout << "套接字不存在" << std::endl;
                exit(10);
            }
        }
    }
    void Accept(std::unordered_map<int, Data *>& mmp, std::unordered_map<int, Data *>::iterator cur)
    {
        // 读取套接字
        int newsock = cur->second->_s->Accept();
        // 设置非阻塞
        Setftl(newsock);
        // 申请套接字对应的缓冲区
        Data *p = new Data(cur->second->_e, cur->second->_s);
        // 插入到哈希中建立映射关系
        mmp.emplace(std::pair<int, Data *>(newsock, p));
        // 插入到epoll模型中
        p->_e->Epoll_Ctl(EPOLL_CTL_ADD, newsock, EPOLLIN | EPOLLET);
        // 打印一下
        std::cout << "插入文件描述符成功" << newsock << std::endl;
    }
    // 读取事件
    void Recv(int sock, std::unordered_map<int, Data *>& mmp, int acceptsock)
    {
        auto cur = mmp.find(sock);
        assert(cur != mmp.end());
        if(sock == acceptsock)
            Accept(mmp, cur);
        else
        {
            char buffer[NUM];
            while (true)
            {
                ssize_t s = recv(cur->first, buffer, NUM, 0);
                if(s > 0)
                {
                    buffer[s] = '\0';
                    cur->second->_inbuffer += buffer;
                    std::cout << cur->second->_inbuffer << std::endl;
                }
                else if(s == 0)
                {
                    std::cout << "客户端退出" << cur->first << std::endl;
                    // 从epoll模型中移除
                    cur->second->_e->Epoll_Ctl(EPOLL_CTL_DEL, cur->first, 0);
                    // 释放套接字所对应的缓冲区
                    delete cur->second;
                    // 关闭文件描述符
                    close(cur->first);
                    break;
                }
                else
                {
                    if(errno == EWOULDBLOCK || errno == EAGAIN)
                    {
                        std::cout << "缓冲区没数据" << std::endl;
                        _por.Cut(cur->second->_inbuffer, cur->second->_outbuffer, cur);
                        break;
                    }
                    else if(errno == EINTR)
                    {
                        std::cout << "被唤醒" << std::endl;
                        break;
                    }
                    else
                    {
                        std::cout << "读取出错误" << std::endl;
                        break;
                    }
                }
            }
        }
    }
    // 写事件
    void Write(int sock, std::unordered_map<int, Data *>& mmp)
    {
        // 防止对端关闭写端导致服务器异常退出
        signal(SIGPIPE, SIG_IGN);
        auto cur = mmp.find(sock);
        assert(cur != mmp.end());
        while (true)
        {
            ssize_t s = send(cur->first, cur->second->_outbuffer.c_str(), cur->second->_outbuffer.size(), 0);
            if(s > 0)
            {
                cur->second->_outbuffer.clear();
                std::cout << "发送成功" << std::endl;
            }
            else
            {
                if (errno == EWOULDBLOCK || errno == EAGAIN)
                {
                    std::cout << "缓冲区没空间" << std::endl;
                    // 关闭写事件
                    SendRespon::OpenofOoff(cur->first, false, cur->second->_e);
                    break;
                }
                else if(errno == EINTR)
                {
                    std::cout << "被唤醒" << std::endl;
                    break;
                }
                else
                {
                    std::cout << "写出错误" << std::endl;
                    break;
                }
            }
        }
    }
private:
    Por _por;
};